﻿using System.ComponentModel;
using System.Runtime.Serialization;

namespace SharpGL.SceneGraph.Effects
{
    /// <summary>
    /// An effect is something that can be applied to a scene element which 
    /// then changes everything in the tree below it. It can be pushed, to apply it
    /// and popped, to restore OpenGL back to the state without the effect.
    /// </summary>
    [TypeConverter(typeof(System.ComponentModel.ExpandableObjectConverter))]
    [DataContract(/*IsReference = true, */Name = "Effect", Namespace = "SharpGL.SceneGraph")]
    public abstract class Effect : INotifyPropertyChanged
    {
        #region Private Consts

        private static readonly string defaultName = "Effect";
        private static readonly bool defaultEnable = true;

        #endregion Private Consts

        #region Private Fields

        /// <summary>
        /// Effect name.
        /// </summary>
        [DataMember()]
        private string name;

        /// <summary>
        /// A value indicating whether this effect is enabled.
        /// </summary>
        [DataMember()]
        private bool enable;

        #endregion Private Fields

        #region Private Methods

        [OnDeserialized]
        private void OnDeserialized(StreamingContext ctx)
        {
            OnDeserialized();
        }

        [OnDeserializing]
        private void OnDeserializing(StreamingContext ctx)
        {
            OnDeserializing();
        }

        #endregion Private Methods

        #region Protected Methods

        /// <summary>
        /// Call this function to do the PropertyChanged event.
        /// </summary>
        protected void NotifyPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

        protected virtual void OnDeserialized()
        {
        }

        protected virtual void OnDeserializing()
        {
        }

        #endregion Protected Methods

        #region Public Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Effect"/> class.
        /// </summary>
        public Effect()
        {
            name = defaultName;
            enable = defaultEnable;
        }

        #endregion Public Constructors

        #region Public Methods

        /// <summary>
        /// Pushes the effect onto the specified parent element.
        /// </summary>
        /// <param name="gl">The OpenGL instance.</param>
        /// <param name="parentElement">The parent element.</param>
        public abstract void Push(OpenGL gl, SceneElement parentElement);

        /// <summary>
        /// Pops the effect off the specified parent element.
        /// </summary>
        /// <param name="gl">The OpenGL instance.</param>
        /// <param name="parentElement">The parent element.</param>
        public abstract void Pop(OpenGL gl, SceneElement parentElement);

        #endregion

        #region Public Events

        [Description("Called when properties of effect has been changed."), Category("SharpGL")]
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion Public Events

        #region Public Properties

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>
        /// The name.
        /// </value>
        [Description("The effect name."), Category("Effect")]
        public string Name
        {
            get { return name; }
            set
            {
                if (name != value)
                {
                    name = value;
                    NotifyPropertyChanged("Name");
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is enabled.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is enabled; otherwise, <c>false</c>.
        /// </value>
        [Description("If true, the object participates in rendering."), Category("Effect")]
        public bool Enable
        {
            get { return enable; }
            set
            {
                if (enable != value)
                {
                    enable = value;
                    NotifyPropertyChanged("Enable");
                }
            }
        }

        #endregion Public Properties
    }
}
